#include <ModuleHandler.h>
#include <iostream>
#include <fstream>
#include <CAENVMElib.h>
#include <TFile.h>
#include <TTree.h>
#include <TBranch.h>
#include <sys/stat.h>
#include <dirent.h>
using namespace std;

const unsigned int ADCBaseAddr = 0x10000;
const unsigned int discBaseAddr = 0x20000;

string DATA_PATH;

struct identifiers {
	ModuleHandler* handler;
	int connect;
	int disconnect;
	int reset;
	int readCycle;
	int writeCycle;
	int dataPath;
	int run;
	int runTime;
	int runPostSamples;
	int runTrigThreshold;
	int stop;
	int download;
	int downloadFile;
	int downloadRun;
	int runStatus;
	int runTriggerCountStatus;
	int runNameStatus;
	int runFileNameStatus;
	int32_t bridgeHandle;
	int32_t opticalHandle;
	int32_t digitizerHandle;
	int test;
	int testNumber;
};

identifiers id;
bool mainLoop;
bool isDone;
bool saveRun;
int numPostSamples;
bool selfTrigger;
bool run;
long stopTime;
int eventsInQueue;
unsigned char*** queue;
pthread_rwlock_t globalLock;
pthread_mutex_t queueMutex;
pthread_cond_t queueReady;
pthread_t saveDataThread;
pthread_t watchADCThread;
string outputFilePath;
ofstream logFile;

string getErrorString(CVErrorCodes error) {
	switch (error) {
	case cvSuccess:
		return "SUCCESS!";
	case cvBusError:
		return "BUS ERROR";
	case cvCommError:
		return "COMMUNICATION ERROR";
	case cvGenericError:
		return "GENERIC ERROR";
	case cvInvalidParam:
		return "INVALID PARAMETERS";
	case cvTimeoutError:
		return "TIMEOUT ERROR";
	}
}

void addToLog(string message) {
	time_t rawTime;
	time(&rawTime);
	tm gmt = *gmtime(&rawTime);
	stringstream ss;
	ss << "(" << gmt.tm_mon + 1 << "/" << gmt.tm_mday << " " << gmt.tm_hour << ":" << gmt.tm_min << ":" << gmt.tm_sec
			<< ")   " << message << endl;

	logFile << ss.str() << flush;
}

string connect() {
	pthread_rwlock_wrlock(&globalLock);
	CVErrorCodes flag = CAENVME_Init(cvV1718, 0, 0, &id.bridgeHandle);
	int32_t bridgeHandle = id.bridgeHandle;
	pthread_rwlock_unlock(&globalLock);

	if (flag != cvSuccess)
		return "Bridge: " + getErrorString(flag);
	else
		CAENVME_SystemReset(bridgeHandle);

	pthread_rwlock_rdlock(&globalLock);
	flag = CAENVME_Init(cvA2818, 0, 0, &id.opticalHandle);
	pthread_rwlock_unlock(&globalLock);

	if (flag != cvSuccess)
		return "Optical: " + getErrorString(flag);

	pthread_rwlock_rdlock(&globalLock);
	flag = CAENVME_Init(cvV2718, 0, 0, &id.digitizerHandle);
	pthread_rwlock_unlock(&globalLock);

	if (flag == cvSuccess)
		return "Success!";
	else
		return "Digitizer: " + getErrorString(flag);
}

string disconnect() {
	pthread_rwlock_rdlock(&globalLock);
	CVErrorCodes flag = CAENVME_End(id.bridgeHandle);
	pthread_rwlock_unlock(&globalLock);

	if (flag != cvSuccess)
		return "Bridge: " + getErrorString(flag);

	pthread_rwlock_rdlock(&globalLock);
	flag = CAENVME_End(id.opticalHandle);
	pthread_rwlock_unlock(&globalLock);

	if (flag != cvSuccess)
		return "Optical: " + getErrorString(flag);

	pthread_rwlock_rdlock(&globalLock);
	flag = CAENVME_End(id.digitizerHandle);
	pthread_rwlock_unlock(&globalLock);

	if (flag == cvSuccess)
		return "Success!";
	else
		return "Digitizer: " + getErrorString(flag);
}

void removeFromQueue(unsigned char***& queue, int& count) {
	unsigned char*** newBuffer = new unsigned char**[count - 1];

	for (int i = 1; i < count; i++)
		newBuffer[i - 1] = queue[i];
	delete queue;

	queue = newBuffer;
	count--;
}

void* saveData(void* sneakyDAQ) {
	TFile* file = new TFile(outputFilePath.c_str(), "update");
	int i;
	for (i = 0; true; i++) {
		std::stringstream runTempSS;
		runTempSS << "run" << i;

		TTree* test = (TTree*) file->Get(runTempSS.str().c_str());
		if (test == NULL)
			break;
	}
	std::stringstream runSS;
	runSS << "run" << i;

	TTree* tree = new TTree(runSS.str().c_str(), runSS.str().c_str());
	tree->SetAutoSave(1000000);
	unsigned short data[8][1024];
	TBranch* dataBranch = tree->Branch("data", &data, "data[8][1024]/s");

	int n = 0;

	addToLog("Started run \"" + runSS.str() + "\" in file \"" + outputFilePath + "\"");
	pthread_rwlock_rdlock(&globalLock);
	id.handler->setStatusValueInt(id.runTriggerCountStatus, n);
	id.handler->setStatusValueString(id.runNameStatus, runSS.str());
	id.handler->setStatusValueString(id.runFileNameStatus, outputFilePath.c_str());
	while (saveRun) {
		pthread_rwlock_unlock(&globalLock);

		pthread_mutex_lock(&queueMutex);
		pthread_cond_wait(&queueReady, &queueMutex);
		while (eventsInQueue > 0) {
			unsigned char** event = queue[0];
			removeFromQueue(queue, eventsInQueue);
			pthread_mutex_unlock(&queueMutex);

			for (i = 0; i < 1024; i++) {
				for (int j = 0; j < 8; j++) {
					int posWithHeader = i * 2 + j * 2048 + 16;
					int blt = (int) posWithHeader / 2048;
					int pos = posWithHeader % 2048;
					data[j][i] = (unsigned short) event[blt][pos] + (unsigned short) ((event[blt][pos + 1] & 0xf) << 8);
				}
			}

			dataBranch->Fill();
			n++;

			for (i = 0; i < 9; i++)
				delete event[i];
			delete event;

			pthread_mutex_lock(&queueMutex);
		}
		pthread_mutex_unlock(&queueMutex);

		pthread_rwlock_rdlock(&globalLock);
		id.handler->setStatusValueInt(id.runTriggerCountStatus, n);
	}
	pthread_rwlock_unlock(&globalLock);

	TBranch* numEventsBranch = tree->Branch("numEvents", &n, "numEvents/I");
	numEventsBranch->Fill();

	tree->AutoSave("SaveSelf");
	file->Close();

	stringstream nss;
	nss << n;
	addToLog("Ended run \"" + runSS.str() + "\" with " + nss.str() + " events");

	pthread_exit(NULL);
}

void addToQueue(unsigned char***& queue, int& count, unsigned char** newEvent) {
	unsigned char*** newBuffer = new unsigned char**[count + 1];

	for (int i = 0; i < count; i++)
		newBuffer[i] = queue[i];
	delete queue;

	newBuffer[count] = newEvent;

	queue = newBuffer;
	count++;
}

void stopRun(bool join = false) {
	pthread_rwlock_rdlock(&globalLock);
	if (!run) {
		pthread_rwlock_unlock(&globalLock);
		return;
	}
	int32_t handle = id.bridgeHandle;
	pthread_rwlock_unlock(&globalLock);
	pthread_rwlock_wrlock(&globalLock);
	run = false;
	pthread_rwlock_unlock(&globalLock);

	unsigned int turnRunOff = 0x0;
	CVErrorCodes flag = CAENVME_WriteCycle(handle, ADCBaseAddr | 0x8100, &turnRunOff, cvA32_S_DATA, cvD32);

	if (join)
		pthread_join(watchADCThread, NULL);
	else
		pthread_detach(watchADCThread);
}

void* watchADC(void*) {
	pthread_rwlock_wrlock(&globalLock);
	isDone = false;
	pthread_rwlock_unlock(&globalLock);

	int32_t bridgeHandle = id.bridgeHandle;
	int32_t opticalHandle = id.opticalHandle;
	int32_t digitizerHandle = id.digitizerHandle;

	unsigned short majThres = 0x13;
	CVErrorCodes flag = CAENVME_WriteCycle(bridgeHandle, discBaseAddr | 0x48, &majThres, cvA32_S_DATA, cvD16);
	if (flag != cvSuccess) {
		stopRun();
	}

	unsigned short chanThres = id.handler->getControlValueInt(id.run, id.runTrigThreshold);
	flag = CAENVME_WriteCycle(bridgeHandle, discBaseAddr | 0x00, &chanThres, cvA32_S_DATA, cvD16);
	if (flag != cvSuccess) {
		stopRun();
	}

	flag = CAENVME_WriteCycle(bridgeHandle, discBaseAddr | 0x02, &chanThres, cvA32_S_DATA, cvD16);
	if (flag != cvSuccess) {
		stopRun();
	}

	unsigned short inhibitPattern = 0x3;
	flag = CAENVME_WriteCycle(bridgeHandle, discBaseAddr | 0x4A, &inhibitPattern, cvA32_S_DATA, cvD16);
	if (flag != cvSuccess) {
		stopRun();
	}

	unsigned short outputPulse = 0xff;
	flag = CAENVME_WriteCycle(bridgeHandle, discBaseAddr | 0x40, &outputPulse, cvA32_S_DATA, cvD16);
	if (flag != cvSuccess) {
		stopRun();
	}

	unsigned int bufferOrg = 0x0a;
	flag = CAENVME_WriteCycle(bridgeHandle, ADCBaseAddr | 0x800c, &bufferOrg, cvA32_S_DATA, cvD32);
	if (flag != cvSuccess) {
		stopRun();
	}

	pthread_rwlock_rdlock(&globalLock);
	int NPSlocal = numPostSamples;
	pthread_rwlock_unlock(&globalLock);

	flag = CAENVME_WriteCycle(bridgeHandle, ADCBaseAddr | 0x8114, &NPSlocal, cvA32_S_DATA, cvD32);
	if (flag != cvSuccess) {
		stopRun();
	}

	pthread_rwlock_rdlock(&globalLock);
	bool STlocal = selfTrigger;
	pthread_rwlock_unlock(&globalLock);

	if (STlocal) {
		unsigned int triggers = 0x800000ff;
		flag = CAENVME_WriteCycle(bridgeHandle, ADCBaseAddr | 0x810c, &triggers, cvA32_S_DATA, cvD32);
		if (flag != cvSuccess) {
			stopRun();
		}
	} else {
		unsigned int triggers = 0xc0000000;
		flag = CAENVME_WriteCycle(bridgeHandle, ADCBaseAddr | 0x810c, &triggers, cvA32_S_DATA, cvD32);
		if (flag != cvSuccess) {
			stopRun();
		}
	}

	unsigned int DAC[8] = { 0x54a8, 0x5240, 0x2700, 0x2700, 0x2700, 0x2700, 0x5341, 0x53a3 };
	for (int i = 0; i < 8; i++) {
		unsigned int address = ADCBaseAddr | 0x1098 + i * 0x100;
		flag = CAENVME_WriteCycle(bridgeHandle, address, &DAC[i], cvA32_S_DATA, cvD32);
		if (flag != cvSuccess) {
			stopRun();
		}
	}

	/*	unsigned int chanConfig = 0x50;
	 flag = CAENVME_WriteCycle(handle, 0x32108000, &chanConfig, cvA32_S_DATA,
	 cvD32);
	 if (flag != cvSuccess) {
	 gdk_threads_enter();
	 daq->log->addToLog("CAENVME_WriteCycle at 0x32108000 return flag:\t"
	 + MessageLog::getVerbalErrorCode(flag));
	 gdk_threads_leave();
	 }*/

	/*	unsigned int VMEControl = 0;
	 flag = CAENVME_ReadCycle(bridgeHandle, baseAddr | 0xEF00, &VMEControl, cvA32_S_DATA, cvD32);
	 if (flag != cvSuccess) {
	 stopRun();
	 }
	 VMEControl &= ~0xf;
	 //	VMEControl |= 0x9;
	 flag = CAENVME_WriteCycle(bridgeHandle, baseAddr | 0xEF00, &VMEControl, cvA32_S_DATA, cvD32);
	 if (flag != cvSuccess) {
	 stopRun();
	 }

	 unsigned int intEventNumber = 0;
	 flag = CAENVME_WriteCycle(bridgeHandle, baseAddr | 0xEF18, &intEventNumber, cvA32_S_DATA, cvD32);
	 if (flag != cvSuccess) {
	 stopRun();
	 }*/

	unsigned int turnRunOn = 0x4;
	flag = CAENVME_WriteCycle(bridgeHandle, ADCBaseAddr | 0x8100, &turnRunOn, cvA32_S_DATA, cvD32);
	if (flag != cvSuccess) {
		stopRun();
	}

	pthread_rwlock_wrlock(&globalLock);
	saveRun = true;
	pthread_rwlock_unlock(&globalLock);

	pthread_rwlock_rdlock(&globalLock);
	pthread_create(&saveDataThread, NULL, &saveData, NULL);
	pthread_rwlock_unlock(&globalLock);

	time_t lastBufferTime = 0;
	time_t lastBERRTime = 0;
	const int wordSize = 4100;

	pthread_rwlock_rdlock(&globalLock);
	id.handler->setStatusValueBool(id.runStatus, true);
	while (run) {
		pthread_rwlock_unlock(&globalLock);

		unsigned int numEventsInADC = 0;
		int lastEventsInBufferErrorTime = 0;
		CVErrorCodes flag = CAENVME_ReadCycle(digitizerHandle, ADCBaseAddr | 0x812c, &numEventsInADC, cvA32_S_DATA,
				cvD32);
		if (flag != cvSuccess) {
			stopRun();
		}

		while (numEventsInADC > 0) {
			unsigned int status = 0;
			flag = CAENVME_ReadCycle(digitizerHandle, ADCBaseAddr | 0xef04, &status, cvA32_S_DATA, cvD32);
			if (flag != cvSuccess) {
				break;
			}

			unsigned int ref = 1;
			bool bits[32];
			for (int i = 0; i < 32; i++)
				bits[i] = ((ref << i) & status);

			if (bits[0]) {
				unsigned char** newEvent = new unsigned char*[0];
				int wordsRead = 0;
				int numBLT = 0;
				while (wordsRead < wordSize) {
					unsigned char** newEventTmp = new unsigned char*[numBLT + 1];
					for (int i = 0; i < numBLT; i++)
						newEventTmp[i] = newEvent[i];
					delete newEvent;
					newEvent = newEventTmp;

					newEvent[numBLT] = new unsigned char[2048];
					for (int i = 0; i < 2048; i++)
						newEvent[numBLT][i] = 0xff;

					int numCharsRetrieved = 0;
					int numCharsRequested = 2048;
					int wordsLeft = wordSize - wordsRead;
					if (wordsLeft < 512)
						numCharsRequested = wordsLeft * 4;

					CVErrorCodes flag = CAENVME_MBLTReadCycle(digitizerHandle, ADCBaseAddr | 0x0000, newEvent[numBLT],
							numCharsRequested, cvA32_U_MBLT, &numCharsRetrieved);
					if (flag != cvSuccess) {
						stopRun();
					} else {
						numBLT++;
						wordsRead += numCharsRetrieved / 4;
					}
				}

				unsigned char** newEventTmp = new unsigned char*[numBLT + 1];
				for (int i = 0; i < numBLT; i++)
					newEventTmp[i] = newEvent[i];
				delete newEvent;
				newEventTmp[numBLT] = NULL;
				newEvent = newEventTmp;

				pthread_mutex_lock(&queueMutex);
				pthread_rwlock_rdlock(&globalLock);
				if (run)
					addToQueue(queue, eventsInQueue, newEvent);
				pthread_rwlock_unlock(&globalLock);
				pthread_cond_broadcast(&queueReady);
				pthread_mutex_unlock(&queueMutex);
			}

			if (bits[1] && time(NULL) - lastBufferTime > 0) {
				lastBufferTime = time(NULL);
			}
			if (bits[2] && time(NULL) - lastBERRTime > 0) {
				lastBERRTime = time(NULL);
			}

			flag = CAENVME_ReadCycle(digitizerHandle, ADCBaseAddr | 0x812c, &numEventsInADC, cvA32_S_DATA, cvD32);
			if (flag != cvSuccess) {
				stopRun();
			}
		}

		//		flag = CAENVME_ReadCycle(opticalHandle, baseAddr | 0xEF00, &VMEControl, cvA32_S_DATA, cvD32);
		//		if (flag != cvSuccess) {
		//			stopRun();
		//		}
		//		VMEControl &= ~0x7;
		//		VMEControl |= 0x9;
		//		flag = CAENVME_WriteCycle(opticalHandle, baseAddr | 0xEF00, &VMEControl, cvA32_S_DATA, cvD32);
		//		if (flag != cvSuccess) {
		//			stopRun();
		//		}


		//		flag = CAENVME_IRQEnable(digitizerHandle, 0x7e);
		//		if (flag != cvSuccess) {
		//			stopRun();
		//		}

		//		CAENVME_IRQWait(digitizerHandle, 0x7e, 10000);

		//		unsigned int vector;
		//		CAENVME_IACKCycle(digitizerHandle, cvIRQ1, &vector, cvD32);

		sleep(1);

		pthread_rwlock_wrlock(&globalLock);
		if (stopTime > 0 && time(NULL) > stopTime) {
			run = false;
			unsigned int turnRunOff = 0x0;
			CVErrorCodes flag =
					CAENVME_WriteCycle(bridgeHandle, ADCBaseAddr | 0x8100, &turnRunOff, cvA32_S_DATA, cvD32);
		}
	}
	pthread_rwlock_unlock(&globalLock);

	pthread_rwlock_wrlock(&globalLock);
	saveRun = false;
	pthread_rwlock_unlock(&globalLock);
	pthread_mutex_lock(&queueMutex);
	pthread_cond_broadcast(&queueReady);
	pthread_mutex_unlock(&queueMutex);
	pthread_join(saveDataThread, NULL);

	pthread_rwlock_wrlock(&globalLock);
	isDone = true;
	id.handler->setStatusValueBool(id.runStatus, false);
	pthread_rwlock_unlock(&globalLock);

	pthread_exit(NULL);
}

void startRun() {
	if (!isDone)
		return;
	pthread_rwlock_rdlock(&globalLock);
	if (run) {
		pthread_rwlock_unlock(&globalLock);
		return;
	}
	pthread_rwlock_unlock(&globalLock);
	pthread_rwlock_wrlock(&globalLock);
	run = true;
	selfTrigger = false;
	numPostSamples = id.handler->getControlValueInt(id.run, id.runPostSamples);
	outputFilePath = DATA_PATH + id.handler->getControlValueString(id.run, id.dataPath);
	stopTime = id.handler->getControlValueInt(id.run, id.runTime);
	if (stopTime > -1)
		stopTime += time(NULL);

	int32_t bridgeHandle = id.bridgeHandle;
	pthread_rwlock_unlock(&globalLock);

	;

	pthread_create(&watchADCThread, NULL, &watchADC, NULL);
}

string makeRunFile(string name, string dataPath) {
	string dataName;
	size_t slash = dataPath.find('/');
	if (slash == string::npos)
		dataName = dataPath;
	else {
		size_t tempSlash = dataPath.find('/', slash + 1);
		while (tempSlash != string::npos) {
			slash = tempSlash;
			tempSlash = dataPath.find('/', slash + 1);
		}
		dataName = dataPath.substr(slash + 1);
	}

	string dir = DATA_PATH + "tmp";
	string path = dir + "/" + name + "From" + dataName;
	dataPath = DATA_PATH + dataPath;	

	TFile* dataFile = new TFile(dataPath.c_str());
	if (dataFile) {
		struct stat st;
		int status = 0;

		if(stat(dir.c_str(),&st) != 0) {
			if(mkdir(dir.c_str(), 0775) != 0)
				status = -1;
		} else if (!S_ISDIR(st.st_mode)) {
			status = -1;
		}
	
		if(status == 0) {
			TFile* file = new TFile(path.c_str(), "recreate");
	
			TTree* runOld = (TTree*) dataFile->Get(name.c_str());
			if (runOld != NULL) {
				TTree* tree = new TTree(name.c_str(), name.c_str());
				tree->SetAutoSave(1000000);
				unsigned short data[8][1024];
				TBranch* dataBranch = tree->Branch("data", &data, "data[8][1024]/s");
				TBranch* oldBranch = runOld->GetBranch("data");
				oldBranch->SetAddress(&data);
				int nEntries = oldBranch->GetEntries();
				for (int i = 0; i < nEntries; i++) {
					oldBranch->GetEntry(i);
					dataBranch->Fill();
				}
				tree->AutoSave("SaveSelf");
			}
	
			dataFile->Close();
			file->Close();
		} else
			path = "";
	}

	return path;
}

string runTest(long testNumber) {
	pthread_rwlock_rdlock(&globalLock);
	int32_t bridgeHandle = id.bridgeHandle;
	pthread_rwlock_unlock(&globalLock);

	unsigned short number = (short) testNumber;
	CVErrorCodes flag = CAENVME_WriteCycle(bridgeHandle, discBaseAddr | 0x40, &number, cvA32_S_DATA, cvD16);

	return getErrorString(flag);
}

void start(ModuleHandler* handler) {
	mainLoop = true;
	isDone = true;
	saveRun = false;
	numPostSamples = 128;
	selfTrigger = false;
	run = false;
	stopTime = 0;
	eventsInQueue = 0;
	queue = new unsigned char**[eventsInQueue];
	outputFilePath = "data.root";

	char* DATA_PATH_ENV = getenv("CRCD_DATA_PATH");
	if (DATA_PATH_ENV)
		DATA_PATH = DATA_PATH_ENV;
	else {
		DATA_PATH = getenv("HOME");
		DATA_PATH += "/CRCDData";
	}

	struct stat st;
	string dir = DATA_PATH;
	DATA_PATH += "/";

	if(stat(dir.c_str(),&st) != 0)
		if(mkdir(dir.c_str(), 0775) != 0)
			return;
	else if (!S_ISDIR(st.st_mode))
		return;

	logFile.open(string(DATA_PATH).append("crcd.log").c_str(), ios_base::app);
	pthread_rwlock_init(&globalLock, NULL);
	pthread_mutex_init(&queueMutex, NULL);
	pthread_cond_init(&queueReady, NULL);
	id.handler = handler;

	handler->setModuleDescription("Cerenkov Radiation Cosmic Detector");

	id.connect = handler->addControlElement("Connect to V1718 bridge", "Connect");
	id.disconnect = handler->addControlElement("Disconnect from V1718 bridge", "Disconnect");

	id.run = handler->addControlElement("Begin collecting data from V1720 digitizer", "Run");
	id.runTime = handler->addControl(id.run, INTCONTROL,
			"Number of seconds the collection should run for;  Set to -1 for no time limit");
	handler->setControlValueInt(id.run, id.runTime, -1);
	id.runPostSamples = handler->addControl(id.run, INTCONTROL,
			"Number of samples/4 to obtain after time of trigger (128 for 512 samples)");
	handler->setControlValueInt(id.run, id.runPostSamples, 128);
	id.runTrigThreshold = handler->addControl(id.run, INTCONTROL, "Trigger thresholds (-mV)");
	handler->setControlValueInt(id.run, id.runTrigThreshold, 50);
	id.dataPath = handler->addControl(id.run, STRINGCONTROL, string("Data file path from ").append(DATA_PATH).c_str());
	handler->setControlValueString(id.run, id.dataPath, "data.root");

	id.stop = handler->addControlElement("Stop current data collection run", "Stop");

	id.download = handler->addControlElement("Download Run", "Download");
	id.downloadFile = handler->addControl(id.download, STRINGCONTROL, "File name");
	id.downloadRun = handler->addControl(id.download, STRINGCONTROL, "Run name");

	id.runStatus = handler->addStatusElement(BOOLSTATUS, "Data collection is running");
	id.runTriggerCountStatus = handler->addStatusElement(INTSTATUS, "Event count");
	id.runNameStatus = handler->addStatusElement(STRINGSTATUS, "Run name (TTree name)");
	id.runFileNameStatus = handler->addStatusElement(STRINGSTATUS, string("File path from ").append(DATA_PATH).c_str());

	id.test = handler->addControlElement("Test", "Test");
	id.testNumber = handler->addControl(id.test, INTCONTROL, "Input number");
	handler->setControlValueInt(id.test, id.testNumber, 0);

	pthread_rwlock_rdlock(&globalLock);
	while (mainLoop) {
		pthread_rwlock_unlock(&globalLock);
		string response = "";
		string path = "";
		int pushId = handler->waitForButtonPush();
		if (pushId == id.connect) {
			response = connect();
		} else if (pushId == id.run) {
			startRun();
			response = "Started";
		} else if (pushId == id.stop) {
			stopRun(true);
			response = "Stopped";
		} else if (pushId == id.disconnect) {
			response = disconnect();
		} else if (pushId == id.download) {
			path = makeRunFile(id.handler->getControlValueString(id.download, id.downloadRun),
					id.handler->getControlValueString(id.download, id.downloadFile));
		} else if (pushId == id.test) {
			response = runTest(id.handler->getControlValueInt(id.test, id.testNumber));
		} else {
			pthread_rwlock_wrlock(&globalLock);
			mainLoop = false;
			pthread_rwlock_unlock(&globalLock);
		}
		handler->giveResponse(response, path);
		pthread_rwlock_rdlock(&globalLock);
	}
	pthread_rwlock_unlock(&globalLock);

	pthread_rwlock_destroy(&globalLock);
	pthread_mutex_destroy(&queueMutex);
	pthread_cond_destroy(&queueReady);
	logFile.close();
	delete[] queue;
}

void stop(ModuleHandler* handler) {
	string path;
	handler->buttonPushed(id.stop, path);
	handler->buttonPushed(id.disconnect, path);
	mainLoop = false;
	handler->abandonButtonWait();
}
